(import
  (except (rnrs base) let-values map error)
  (only (guile) lambda* λ command-line)
  (srfi srfi-1)
  (fileio))


(define find-number
  (λ (numbers)
    (let iter ([factors-l1 numbers]
               [factors-l2 numbers]
               [factors-l3 numbers]
               [rest-numbers numbers])
      (cond
       [(null? factors-l1) (error "tried all combinations")]
       [(null? factors-l2) (iter (cdr factors-l1)
                                 numbers
                                 numbers
                                 (cdr rest-numbers))]
       [(null? factors-l3) (iter factors-l1
                                 (cdr factors-l2)
                                 numbers
                                 rest-numbers)]
       [else
        (let ([fac1 (car factors-l1)]
              [fac2 (car factors-l2)]
              [fac3 (car factors-l3)])
          (cond
           [(= (+ fac1 fac2 fac3) 2020) (* fac1 fac2 fac3)]
           [else
            (iter factors-l1
                  factors-l2
                  (cdr factors-l3)
                  rest-numbers)]))]))))


(define find-numbers-efficiently
  (λ (numbers)
    "The following approach is adapted from
https://en.wikipedia.org/wiki/3SUM."
    (define numbers-sorted (list->vector (sort numbers <)))
    (define num-of-elements (vector-length numbers-sorted))
    (let for-loop ([ind1 0])
      (cond
       ;; end condition for outer loop
       ;; only check until n - 2, because above that there
       ;; are no sufficient numbers available to form a
       ;; triplet of the wanted sum
       [(< ind1 (- num-of-elements 2))
        (let ([addend1 (vector-ref numbers-sorted ind1)])
          (let while-loop ([current-start (+ ind1 1)]
                           [current-end (- num-of-elements 1)])
            (cond
             ;; condition for inner loop
             [(< current-start current-end)
              (let* ([addend2 (vector-ref numbers-sorted current-start)]
                     [addend3 (vector-ref numbers-sorted current-end)]
                     [summed (+ addend1 addend2 addend3)])
                (cond
                 [(= summed 2020)
                  (simple-format (current-output-port) "found triplet: ~a ~a ~a\n" addend1 addend2 addend3)
                  (* addend1 addend2 addend3)]
                 ;; if the sum is too high, we need to add lower values in
                 ;; order to have any chance of summing to the target value,
                 ;; so we decrease the end index by 1.
                 [(> summed 2020)
                  (while-loop current-start (- current-end 1))]
                 ;; if the sum is too low, we need to add higher values in
                 ;; order to have any chance of summing to the target value,
                 ;; so we increase the start index by 1.
                 [else
                  (while-loop (+ current-start 1) current-end)]))]
             [else
              (for-loop (+ ind1 1))])))]
       [else (error "number not found")]))))


(define main
  (λ (cmd-line-args)
    (let* ([lines (get-lines-from-file (cadr cmd-line-args))]
           [numbers (map string->number lines)])
      (find-numbers-efficiently numbers))))


(simple-format (current-output-port)
               "~a\n"
               (main (command-line)))
